﻿using System;
using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;
using Xenta.Entities;
using Xenta.Operations;
using Xenta.Utils;

namespace Xenta.Tests
{
    [TestFixture]
    public class OperationProcessorImplTests : InfrastructureTest
    {
        private IOperationProcessor Processor
        {
            get;
            set;
        }

        [Test]
        public void Can_Execute_SimpleOperation()
        {
            Processor.Initialize(new WorkingContext
            {
                CID = Guid.NewGuid(),
                IsSafe = true
            });

            int result = Processor.Execute(new CustomOperation<Int32>(uow => 1 + 1)).Result;
            Processor.Rollback();

            Assert.AreEqual(2, result);
        }

        [Test]
        public void Throw_Exc_If_Not_Init()
        {
            Assert.Throws<ApplicationException>(() =>
            {
                Processor.Execute(new CustomOperation<Int32>(uow => 1 + 1));
                Processor.Rollback();
            });
        }

        public override void TestSetUp()
        {
            base.TestSetUp();

            Processor = new OperationProcessorImpl(Infrastructure);
        }

        public override void TestTearDown()
        {
            Processor.Dispose();
            Processor = null;

            base.TestTearDown();
        }
    }

    [TestFixture]
    public class UnitOfWorkImplTests : InfrastructureTest
    { 
        private IUnitOfWork UoW
        {
            get;
            set;
        }

        protected override IEnumerable<Type> ModelMappings
        {
            get
            {
                return NHMappings.CORE;
            }
        }

        public override void TestSetUp()
        {
            base.TestSetUp();

            UoW = new UnitOfWorkImpl(SessionFactory);
        }

        public override void TestTearDown()
        {
            UoW.Dispose();
            UoW = null;

            base.TestTearDown();
        }

        [Test]
        public void Can_Crud_Statefull()
        {
            int id = (int)UoW.Create(new LogRecordEntity
            {
                Source = "source",
                Description = "desription",
                Text = "text"
            });

            var entity = UoW.Get<LogRecordEntity>(id);

            Assert.NotNull(entity);
            entity.Source.AreEqualIgnoringCase("source");
            entity.Description.AreEqualIgnoringCase("desription");
            entity.Text.AreEqualIgnoringCase("text");

            entity.Source = entity.Source.Reverse();
            entity.Description = entity.Description.Reverse();
            entity.Text = entity.Text.Reverse();

            UoW.Update(entity);

            entity = UoW.Get<LogRecordEntity>(id);

            Assert.NotNull(entity);
            entity.Source.AreEqualIgnoringCase("source".Reverse());
            entity.Description.AreEqualIgnoringCase("desription".Reverse());
            entity.Text.AreEqualIgnoringCase("text".Reverse());

            UoW.Delete(entity);

            entity = UoW.Get<LogRecordEntity>(id);

            Assert.Null(entity);

            UoW.Rollback();
        }

        [Test]
        public void Can_Crud_Stateless()
        {
            int id = (int)UoW.CreateStateless(new LogRecordEntity
            {
                Source = "source",
                Description = "desription",
                Text = "text",
                CreatedOn = DateTime.UtcNow,
                UpdatedOn = DateTime.UtcNow
            });
            UoW.Commit();

            var entity = UoW.GetStateless<LogRecordEntity>(id);

            Assert.NotNull(entity);
            entity.Source.AreEqualIgnoringCase("source");
            entity.Description.AreEqualIgnoringCase("desription");
            entity.Text.AreEqualIgnoringCase("text");

            entity.Source = entity.Source.Reverse();
            entity.Description = entity.Description.Reverse();
            entity.Text = entity.Text.Reverse();

            UoW.UpdateStateless(entity);
            UoW.Commit();

            entity = UoW.GetStateless<LogRecordEntity>(id);

            Assert.NotNull(entity);
            entity.Source.AreEqualIgnoringCase("source".Reverse());
            entity.Description.AreEqualIgnoringCase("desription".Reverse());
            entity.Text.AreEqualIgnoringCase("text".Reverse());

            UoW.DeleteStateless(entity);
            UoW.Commit();

            entity = UoW.GetStateless<LogRecordEntity>(id);

            Assert.Null(entity);

            UoW.Rollback();
        }

        [Test]
        public void Dont_Allow_Suspend_If_Not_CommitOrRollback()
        {
            UoW.Get<LogRecordEntity>(0);
            Assert.Throws<ApplicationException>(() => UoW.Suspend());
        }
    }

    [TestFixture]
    public class UnitOfWorkFactoryImplTests : InfrastructureTest
    {
        private IUnitOfWorkFactory Factory
        {
            get;
            set;
        }

        public override void TestSetUp()
        {
            base.TestSetUp();
            Factory = new UnitOfWorkFactoryMock(SessionFactory);
        }

        public override void TestTearDown()
        {
            Factory.Dispose();
            Factory = null;
            base.TestTearDown();
        }

        [Test]
        public void Can_Produce()
        {
            var uow = Factory.Produce();
            Assert.NotNull(uow);
            uow.Dispose();
        }
    }

    [TestFixture]
    public class UnitOfWorkPoolImplTests : InfrastructureTest
    {
        private IUnitOfWorkPool Pool
        {
            get;
            set;
        }

        public override void TestSetUp()
        {
            base.TestSetUp();
            var factory = new UnitOfWorkFactoryMock(SessionFactory);
            Pool = new UnitOfWorkPoolImpl(factory);
        }

        public override void TestTearDown()
        {
            Pool.Dispose();
            Pool = null;

            base.TestTearDown();
        }

        [Test]
        public void Can_LeaseAndReturn()
        {
            var guid = Guid.NewGuid();
            var leasehold = Pool.Lease(guid);

            Assert.NotNull(leasehold);
            leasehold.Guid.AreEqual(guid);

            Pool.Return(leasehold);
        }

        [Test]
        public void Does_Lease_Same()
        {
            var guid = Guid.NewGuid();
            var leasehold1 = Pool.Lease(guid);

            Assert.NotNull(leasehold1);
            leasehold1.Guid.AreEqual(guid);

            Pool.Return(leasehold1);

            var leasehold2 = Pool.Lease(guid);

            Assert.NotNull(leasehold2);
            leasehold2.Guid.AreEqual(guid);
            leasehold2.AreSame(leasehold1);

            Pool.Return(leasehold2);
        }

        [Test]
        public void Does_Dispose_Returned()
        {
            var guid = Guid.NewGuid();
            var leasehold1 = Pool.Lease(guid);

            Assert.NotNull(leasehold1);
            leasehold1.Guid.AreEqual(guid);

            Pool.Return(leasehold1);

            Thread.Sleep(UnitOfWorkPoolImpl.LEASE_TIME * 2);
            Assert.Null(leasehold1.UoW);

            var leasehold2 = Pool.Lease(guid);

            Assert.NotNull(leasehold2);
            leasehold2.Guid.AreEqual(guid);
            leasehold2.AreNotSame(leasehold1);

            Pool.Return(leasehold2);
        }
    }
}
